from typing import Any, Optional
from types import FunctionType
import uuid
from django.db import models
from django.db.models import Q, F

from django.utils import timezone
from django.utils.timezone import datetime, timedelta
from core.log import logging
from django.db.models import Value
from django.db.models.functions import Replace
from api.models.Choices.choices_collects import *
"""
use timezonne.now() get datetime struct in service.
"""
from TDhelper.generic.crypto.MD5 import MD5

# Create your models here.

     
class auto_id(models.Model):
    id = models.AutoField(primary_key=True, db_index=True, help_text="index id.")
    uuid = models.UUIDField(db_index=True, default=uuid.uuid1, blank=True)

    def __str__(self):
        return self.__class__.__name__

    class Meta:
        abstract = True
        ordering = ["-id"]

    @classmethod
    def get_by_uuid(cls, uuid):
        try:
            return cls.objects.get(uuid__exact=uuid)
        except:
            return None


class base_date(models.Model):
    created_time = models.DateTimeField(
        editable=False, auto_now_add=True, help_text="create datetime."
    )
    last_update_time = models.DateTimeField(
        editable=False, auto_now=True, help_text="last update datetime."
    )

    def __str__(self):
        return self.__class__.__name__

    class Meta:
        abstract = True


class mangent_helper(models.Model):
    enable = models.BooleanField(
        help_text="启禁用(enable[1]|disable[0]),default enable", default=True
    )
    is_delete = models.BooleanField(
        default=False, help_text="record delete state.default False"
    )

    def __str__(self):
        return self.__class__.__name__

    def logical_deletion(self):
        self.is_delete = True
        self.save(force_update=True, update_fields=["is_delete"])
        for o in self.__dict__:
            if isinstance(o, models.ForeignKey):
                if "logical_deletino" in self.__dict__:
                    self.__dict__[o].logical_deletion()
                else:
                    self.__dict__[o].delete()

    def enable_or_disable(self):
        self.enable = not self.enable
        self.save(force_update=True, update_fields=["enable"])

    class Meta:
        abstract = True
    
class trace_helper(auto_id, mangent_helper):
    parent = models.IntegerField(help_text="上级", default=0, blank=True)
    trace = models.TextField(help_text="层级追溯辅助字段", default=None, null=True, blank=True)
    depth= models.IntegerField(help_text="基于ROOT的所在深度", default=0,blank=True)

    def __str__(self):
        return self.__class__.__name__

    class Meta:
        abstract = True

    def save_and_generate_trace(self, **kwargs):
        '''save node and generate trace string and depth.
        
            params:
                **kwargs: <cls, dict>, query parent node base on condition. if none then query base on pk.
                
            returns: 
            
                success: None. 
                    
                    insert mode: get execute state by self.pk.
                    
                    update mode: if no raise error, then execute success.
                
                Exception: raise error.
        
        '''
        if self.parent != 0:
            try:
                if not kwargs:
                    o = self.__class__.objects.get(id__exact=self.parent)
                else:
                    o = self.__class__.objects.get(**kwargs)
                if not o.is_delete:
                    if o.enable:
                        try:
                            self.save()
                            if self.id:
                                # generate trace string.
                                self.trace = "".join([str(o.trace), str(self.id), ","])
                                try:
                                    # calculate self's depth base on root node.
                                    self.depth = self.trace.rstrip(',').split(
                                        ',').__len__()-2
                                except:
                                    self.depth = 0
                                self.save(force_update=True,
                                          update_fields=["trace", "depth"])
                            else:
                                raise Exception("save error, pk is none.")
                        except Exception as e:
                            raise e
                    else:
                        raise Exception("paren record has disable.")
                else:
                    raise Exception("parent record has deleted.")
            except Exception as e:
                raise Exception("not found parent.")
        else:
            try:
                self.save()
                if self.id:
                    # generate trace string.
                    self.trace = "".join(["0", ",", str(self.id), ","])
                    try:
                        # calculate self's depth base on root node.
                        self.depth = self.trace.rstrip(',').split(',').__len__()-2
                    except:
                        self.depth = 0
                    self.save(force_update=True,update_fields=["trace", "depth"])
            except Exception as e:
                raise e

    def update_with_childs(self, update_trace=True):
        '''when self's trace string changed, synchronous childs's trace string; 
        
            params: 
            
                update_trace: <cls, boolen>
                
            returns: 
                success: <cls, int>, total number of updated records.
                
                Exception: raise error.
        '''
        trace_backup = self.trace
        result = 0
        if update_trace:
            if self.parent != 0:
                try:
                    o = self.__class__.objects.get(pk=self.parent)
                    self.trace = "".join([o.trace, str(self.id), ","])
                except Exception as e:
                    raise e
            else:
                self.trace = "".join(["0,",str(self.id), ","])
        self.save(force_update=True)
        result += 1
        if update_trace:
            result += self.__class__.objects.filter(
                trace__startswith=trace_backup
            ).update(trace=Replace("trace", Value(trace_backup), Value(self.trace)))
        return result

        
    @classmethod
    def get_branch(cls, pk:int=0, depth:int=0):
        '''get node with childs base on pk.
        
        params:
        
            pk: <cls, int>, pk.
            
            depth: <cls, int>, query depth for childs by value, if value==0 return all childs; default 0;
            
        returns: 
            success: <cls, queryset>
            
            Excepton: <cls, list>, empty list.
        '''
        pk= pk if isinstance(pk,int) else int(pk)
        depth= depth if isinstance(depth,int) else int(depth)
        try:
            o= cls.objects.get(pk__exact=pk)
            if depth>0:
                try:
                    return cls.objects.filter(
                        Q(trace__startswith=o.trace)&
                        Q(is_delete__exact=False)&
                        Q(enable__exact=True)&
                        Q(depth__gte=o.depth)&
                        Q(depth__lte=o.depth+depth)
                    )
                except Exception as e:
                    return []
            else:
                return cls.objects.filter(
                    Q(trace__startswith=o.trace)&
                    Q(is_delete=False)&
                    Q(enable=True)
                )
        except Exception as e:
            logging.error(e)
            return []

    def get_parent_pk_list(self):
        '''self's parent iterate

            returns: 
            
                success: <cls, list>, iterate object.
                
                Exception: <cls, list>, empty list.
        '''
        try:
            m_iter= [int(v) for v in self.trace.rstrip(',').split(",")]
            m_iter.reverse()
            return m_iter
        except Exception as e:
            return []


    def logical_deletion(self):
        '''logical delete record.
        
            returns: 
                success: <cls, int>, total number of deleted records.

                Exception: <cls, int>, 0
        '''
        try:
            o = self.__class__.objects.filter(trace__startswith=self.trace).update(
                is_delete=self.is_delete
            )
            return o
        except Exception as e:
            logging.error(e)
            return 0

    def enable_or_disable(self):
        try:
            self.enable = not self.enable
            o = 0
            if not self.enable:
                o = self.__class__.objects.filter(
                    Q(trace__startswith=self.trace)
                ).update(enable=self.enable)
            else:
                o = self.__class__.objects.filter(Q(trace__exact=self.trace)).update(
                    enable=self.enable
                )
            return o
        except Exception as e:
            logging.error(e)
            return 0
